#include <stdlib.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <signal.h>

#include "global.h"
#include "constantes.h"
#include "sockets.h"
#include "configuracion.h"
#include "listas.h"
#include "NIPC.h"
#include "sincronizador.h"
#include "hiloDisco.h"


extern int owner;

void * HiloDeDisco (void * pNodoDisco)
{

	fd_set set_socketsHiloParaDisco;
	int iMayorSocket = 0 ;	
	int idSinc;
	int payload;
	char *sAux=(char *)malloc(20);
	char *sNumero=(char *)malloc(20);
	disco * pDisco = (disco*) pNodoDisco;
	char buffer[512];
	char buffer_sect[20];
	char buffer_id[20];
	int flag;
	int ret;
	int contEnviados=0;
	int tamanio_buff;
	
	
	
	while(1)
	{
		
		/*FD_ZERO(&set_socketsHiloParaDisco);
		FD_SET(pDisco->iSocketHDAParaDA, &set_socketsHiloParaDisco);
		iMayorSocket = pDisco->iSocketHDAParaDA;	
		select(iMayorSocket + 1,&set_socketsHiloParaDisco, NULL, NULL,NULL);
		*/
		
		/******RTA DEL DISCO*******/
		/*if( FD_ISSET(pDisco->iSocketHDAParaDA,&set_socketsHiloParaDisco ) > 0 )	
		{*/
			
			/*Recibo el IDDescriptor del pedido*/
			InfoIPC rInfoIPC;
			if(recibirIPC(pDisco->iSocketHDAParaDA,&rInfoIPC)<0){
				printf("El disco %s se ha desconectado \n",pDisco->sNombre);
				reallocateoperations(pDisco);
				return NULL;
			}
			
			
			/*printf("rInfoIPC.payload %s \n",rInfoIPC.payload);*/
			
			char *payload = (char *) malloc (sizeof(char) * rInfoIPC.payLoadLength);
			memcpy(payload, rInfoIPC.payload, rInfoIPC.payLoadLength);
			
			if(strchr(rInfoIPC.payload, ';') != NULL)
			{
				strcpy(buffer, strtok(rInfoIPC.payload, ";"));
				strcpy(buffer_id, strtok(buffer, ","));
				strcpy(buffer_sect, strtok(NULL, "\r\n\0"));
			}
			else
				continue;
	

			/*printf("rInfoIPC.payload 2 %s \n",payload);*/
	
			int sector_num = atoi(buffer_sect);
			
			pedido* nodoaAtender;
			
			
			if((rInfoIPC.payLoadDescriptor == RTA_READ_SECTOR_REQUEST)||(rInfoIPC.payLoadDescriptor == RTA_WRITE_SECTOR_REQUEST)){
				
				if(pthread_mutex_lock(&mutex)!=0){
					perror("pthread_mutex_lock");
					exit(EXIT_FAILURE);
				}
				
				
				nodoaAtender = (pedido *)BuscarDescriptorEnListaDeDisco(colaGlobal, buffer_id);
			
				if(pthread_mutex_unlock(&mutex)!=0){
				perror("pthread_mutex_unlock");
				exit(EXIT_FAILURE);
				}
				
		
				
				if(rInfoIPC.payLoadDescriptor==RTA_READ_SECTOR_REQUEST){
				
					printf("Se recibio la confirmacion de lectura del sector %s por parte del disco %s \n",buffer_sect,pDisco->sNombre);
					
					/*Le envio al cliente FSS el numero de sector respondido*/
					InfoIPC rInfoIPC3;
					rInfoIPC3.payLoadLength= strlen(buffer_sect) + 1;
					generarStructIPC(buffer_sect ,rInfoIPC.payLoadDescriptor,&rInfoIPC3);
					enviarIPC(atoi(nodoaAtender->sSocketPorElCualResponder),&rInfoIPC3);
					free(rInfoIPC3.payload);
					
					/*Le envio al cliente FSS la data correspondiente*/
					InfoIPC rInfoIPC4;
					rInfoIPC4.payLoadLength=512;
					generarStructIPC(payload+strlen(buffer_id) + 1 + strlen(buffer_sect) + 1  ,rInfoIPC.payLoadDescriptor,&rInfoIPC4);
					enviarIPC(atoi(nodoaAtender->sSocketPorElCualResponder),&rInfoIPC4);				
					free(rInfoIPC4.payload);
					
					if(pthread_mutex_lock(&mutex)!=0){
						perror("pthread_mutex_lock");
						exit(EXIT_FAILURE);
					}
					
					SacarNodoDescriptoresDeLista2(&nodoaAtender,&(colaGlobal),&(colaGlobal_Ult));
				
					if(pthread_mutex_unlock(&mutex)!=0){
						perror("pthread_mutex_unlock");
						exit(EXIT_FAILURE);
					}
					free(nodoaAtender);
				
					
				
					pDisco->pendings--;	
				}
				
				
				if(rInfoIPC.payLoadDescriptor==RTA_WRITE_SECTOR_REQUEST){
				
					/**************PEDIDO COMUN***************/
					
					
					/*SACO DE LISTA DE PEDIDOS DEL DISCO*/
					if(pthread_mutex_lock(&mutex)!=0){
							perror("pthread_mutex_lock");
							exit(EXIT_FAILURE);
					}
					
					SacarNodoDescriptoresDeLista2(&nodoaAtender,&(colaGlobal),&(colaGlobal_Ult));
					
					if(pthread_mutex_unlock(&mutex)!=0){
							perror("pthread_mutex_unlock");
							exit(EXIT_FAILURE);
					}
					
					/*BUSCO EN COLA DE CONTROL*/
					if(pthread_mutex_lock(&mutex2)!=0){
						perror("pthread_mutex_lock");
						exit(EXIT_FAILURE);
					}
					
					t_control *nodoCont =(t_control *)BuscarDescriptorEnColaControl(colaControl,nodoaAtender->sDescriptorID_delPedido);
					
					if(pthread_mutex_unlock(&mutex2)!=0){
						perror("pthread_mutex_unlock");
						exit(EXIT_FAILURE);
					}
				
					imprimirCola5(colaControl);
					printf("nodoCont->discosAEspejar %i \n",nodoCont->discosAEspejar);
					
					if(nodoCont->discosAEspejar>1)
					{
					
						printf("Se recibio una respuesta de escritura\n");

						/*ACTUALIZO DISCOS A ESPEJAR EN COLA DE CONTROL CONTROL*/
						if(pthread_mutex_lock(&mutex2)!=0){
							perror("pthread_mutex_lock");
							exit(EXIT_FAILURE);
						}
						
						nodoCont->discosAEspejar--;
						
						if(pthread_mutex_unlock(&mutex2)!=0){
							perror("pthread_mutex_unlock");
							exit(EXIT_FAILURE);
						}
						
						
					}
					else
					{					
						printf("Se recibio la confirmacion de escritura del sector %i por parte de todos los discos existentes \n",nodoaAtender->numeroSector);
						
						/*SACO DE COLA DE CONTROL*/
						if(pthread_mutex_lock(&mutex2)!=0){
							perror("pthread_mutex_lock");
							exit(EXIT_FAILURE);
						}
						
						SacarDeColaControl2(&nodoCont,&colaControl,&colaControl_Ult);
				
						if(pthread_mutex_unlock(&mutex2)!=0){
							perror("pthread_mutex_unlock");
							exit(EXIT_FAILURE);
						}
					
						free(nodoCont);
						/*Le envio al cliente FSS el numero de sector respondido*/
						InfoIPC rInfoIPC3;
						rInfoIPC3.payLoadLength= strlen(buffer_sect) + 1;
						generarStructIPC(buffer_sect ,rInfoIPC.payLoadDescriptor,&rInfoIPC3);
						enviarIPC(atoi(nodoaAtender->sSocketPorElCualResponder),&rInfoIPC3);
					}
				
					pDisco->pendings--;	
					free(nodoaAtender);
				
				}
				
				free(rInfoIPC.payload);
				
			}
		
			else
			{
				disco* discoSinc=(disco*)BuscarDiscoPorNombre("Disco2");
				/*printf("Voy a sincronizar el %s \n",discoSinc->sNombre);*/
	
				if(rInfoIPC.payLoadDescriptor==FINSINCRO){
					printf("Se finalizo la sincronizacion del disco %s\n", discoSinc->sNombre);
					discoSinc->Estado = LISTO;
					printf("DISCO LISTO\n");
				}
				else{
					contEnviados++;
					
				
					/*tamanio_buff = strlen(buffer_id) + 1 + strlen(buffer_sect) + 1 + 512 + 1;
					char* buffer2 = (char *) malloc (sizeof(char) * tamanio_buff);
					strcpy(buffer2, buffer_id);
					strcat(buffer2, ",");
					strcat(buffer2, buffer_sect);
					strcat(buffer2, ";");
					memcpy(buffer2 + strlen(buffer_id) + 1 + strlen(buffer_sect) + 1, rInfoIPC.payload , 512);*/

					
					InfoIPC rInfoIPC33;
					/*rInfoIPC33.payLoadLength=tamanio_buff;*/
					rInfoIPC33.payLoadLength=rInfoIPC.payLoadLength;
					generarStructIPC(payload,WRITE_SECTORSYNC_REQUEST,&rInfoIPC33);
					enviarIPC(discoSinc->iSocketHDAParaDA,&rInfoIPC33);	
					free(rInfoIPC33.payload);
			
	
	
					/*printf("Envie %i sectores \n",contEnviados);*/
					free(rInfoIPC.payload);
					/*free(buffer);*/
					
				}
			}
		
		/*}*/
	}
	
	free(sAux);
	free(sNumero);
}

void reallocateoperations(disco *dsk) {
	
	int ret;
	int flag=0;
	int noDisco=0;
	
	SacarDeLista_DA(&dsk);
	
	printf("En reallocate operations \n");
	imprimirCola2(colaGlobal);
	printf("------------------------ \n");
	
	while((flag!=1)&&(noDisco!=-1)){
		ret=pthread_mutex_trylock(&mutex);	
		if (ret==0)
		{
			pedido* lib=(pedido *)BuscarPorDisco(colaGlobal,dsk->sNombre);
			if(lib!=NULL){
				if((lib->operacion==0)){
					lib->estado=0;
					noDisco=asignarPedido(&lib,1);
				}
			}
			else
				flag=1;
			
			if(pthread_mutex_unlock(&mutex)!=0){
				perror("pthread_mutex_lock");
				exit(EXIT_FAILURE);
			}
		}
	}
	
	
	if(noDisco==-1){
	
		printf("NODISCO\n");
		
		if(estaVacia2(ListaDiscos)){
		
			ret=pthread_mutex_trylock(&mutex);	
			if (ret==0)
			{
			
				while(!estaVacia(colaGlobal)){
				
					pedido *aux=colaGlobal;
					colaGlobal=colaGlobal->Sgt;
					free(aux);
				}	
				
				
				if(pthread_mutex_unlock(&mutex)!=0){
						perror("pthread_mutex_lock");
						exit(EXIT_FAILURE);
				}
				
				printf("ColaFinal\n");
				imprimirCola2(colaGlobal);
				
				return;
				
			}
		}
		
		else{
		
			flag=0;
			
			while((flag!=1)){
				ret=pthread_mutex_trylock(&mutex);	
				if (ret==0)
				{
					pedido* lib=(pedido *)BuscarPorDisco(colaGlobal,dsk->sNombre);
					if(lib!=NULL){
						if((lib->operacion==0)){
							SacarNodoDescriptoresDeLista2(&lib,&(colaGlobal),&(colaGlobal_Ult));
							free(lib);
						}
					}
					else
						flag=1;
			
					if(pthread_mutex_unlock(&mutex)!=0){
						perror("pthread_mutex_lock");
						exit(EXIT_FAILURE);
					}
					
				}	
			}
	
		}
	}
	
	printf("ColaFinal\n");
	imprimirCola2(colaGlobal);
	
	free(dsk);
	
}




int VerificarExistenciaDiscoSincronizados()
{
    struct DiscoConocido * pAuxDisco;
    int icantidad = 0;
	InfoIPC *infoIPC;
	
    pAuxDisco = ListaDiscos;
    while(pAuxDisco != NULL)
    {
	if ((pAuxDisco->Estado == LISTO)  || (pAuxDisco->Estado == SINCRONIZANDO_LEYENDO))
	    icantidad ++;
	pAuxDisco = pAuxDisco->Sgt;
    }

    return icantidad;
}


void intacad ( int n , char* cadena)
{	
    int j=0;
    int i=0;
	char aux;

	do
    {
		cadena[i] = (n % 10) + '0';    
		n=n/10;
		i++;
	}while (n > 0);

    cadena[i] = '\0';
    i--;

	while(i>j)
	{
		aux=cadena[i];
		cadena[i]=cadena[j];
		cadena[j]=aux;
		i--;
		j++;
	}

}


